package com.ytjj.qmyx.supplychain.api.ceb.filter;


import com.alibaba.fastjson.JSONObject;
import com.ytjj.qmyx.supplychain.api.ceb.config.IgnoreUrlsConfig;
import com.ytjj.qmyx.supplychain.common.api.ResultCode;
import com.ytjj.qmyx.supplychain.common.exception.ApiException;
import com.ytjj.qmyx.supplychain.common.utils.IPUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.map.LinkedMap;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Map;


/**
 * 签名认证拦截
 * 
 */
@Component
@Slf4j
public class AuthGlobalFilter implements Filter {

    private static final String CONTENT_TYPE_JSON = "json";
    private final IgnoreUrlsConfig ignoreUrlsConfig;

    public AuthGlobalFilter(IgnoreUrlsConfig ignoreUrlsConfig) {
        this.ignoreUrlsConfig = ignoreUrlsConfig;
    }

    private static final String SECRET_KEY = "xxxxx";

    /**
     * 鉴权参数校验
     *
     * @param sourceSign
     * @param sign
     */
    private void authenticationSign(String sourceSign, String sign) {
        if (null == sign || !sign.equals(sourceSign)) {
            throw new ApiException(ResultCode.SIGN_FAILED);
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        if (ignoreUrlsConfig.getSwitchFlag().equals(0)) {
            filterChain.doFilter(servletRequest, servletResponse);
        }else {
            if (!(servletRequest instanceof HttpServletRequest) || !(servletResponse instanceof HttpServletResponse)) {
                throw new ServletException("拦截器仅支持HTTP请求");
            }
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            if (ignoreUrlsConfig.getIpSwitchFlag().equals(1)) {
                //ip验证
                authenticationIp(httpServletRequest);
            }

            log.debug(JSONObject.toJSONString(httpServletRequest.getRequestURI()));
            //白名单请求直接放行
            PathMatcher pathMatcher = new AntPathMatcher();
            boolean matchResult = false;
            for (String path : ignoreUrlsConfig.getUrls()) {
                if (pathMatcher.match("/**" + path, httpServletRequest.getServletPath())) {
                    matchResult = true;
                    break;
                }
            }
            log.debug("===matchResult==={}", matchResult);
            if (matchResult) {
                filterChain.doFilter(servletRequest, servletResponse);
            } else {
                String sign = generateSign(httpServletRequest);
                String sourceSign = httpServletRequest.getHeader("sign");
                log.info("===sign={}, sourceSign={}", sign, sourceSign);
                authenticationSign(sourceSign, sign);
                filterChain.doFilter(servletRequest, servletResponse);
            }
        }
    }

    private void authenticationIp(HttpServletRequest httpServletRequest) {
        String addr = IPUtil.getIpAddress(httpServletRequest);
        log.info("authenticationIp===addr==={}", addr);
        if (!ignoreUrlsConfig.getIps().contains(addr)) {
            throw new ApiException(ResultCode.IP_FAILED);
        }
    }

    //生成sign签名
    private String generateSign(HttpServletRequest httpServletRequest) {
        String timestamp = httpServletRequest.getHeader("timestamp");
        String source = httpServletRequest.getHeader("source");
        String method = httpServletRequest.getMethod();

        Map<String,String> map = new LinkedMap();
        map.put("contentType",CONTENT_TYPE_JSON);
        map.put("method",method);
        map.put("timestamp",timestamp);
        map.put("source",source);
        map.put("SECRET_KEY",SECRET_KEY);
        log.info(JSONObject.toJSONString(map));
        String sign = DigestUtils.md5Hex(JSONObject.toJSONString(map).toUpperCase());
        return sign;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ServletContext context = filterConfig.getServletContext();
        ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(context);
    }

    @Override
    public void destroy() {

    }

    public static void main(String[] args) {

        String timestamp = "1646978437116";
        String source = "wms";
        String method = "POST";

        Map<String,String> map = new LinkedMap();
        map.put("contentType",CONTENT_TYPE_JSON);
        map.put("method",method);
        map.put("timestamp",timestamp);
        map.put("source",source);
        map.put("SECRET_KEY",SECRET_KEY);
        String sign = DigestUtils.md5Hex(JSONObject.toJSONString(map).toUpperCase());
        System.out.println(sign);
        String sign2 = org.springframework.util.DigestUtils.md5DigestAsHex(JSONObject.toJSONString(map).toUpperCase().getBytes());
        System.out.println(sign2);
        System.out.println(getMD5(JSONObject.toJSONString(map).toUpperCase()));
    }
    public static String getMD5(String message) {
        String md5 = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");  // 创建一个md5算法对象
            byte[] messageByte = message.getBytes("UTF-8");
            byte[] md5Byte = md.digest(messageByte);              // 获得MD5字节数组,16*8=128位
            md5 = bytesToHex(md5Byte);                            // 转换为16进制字符串
        } catch (Exception e) {
            e.printStackTrace();
        }
        return md5;
    }
    // 二进制转十六进制
    public static String bytesToHex(byte[] bytes) {
        StringBuffer hexStr = new StringBuffer();
        int num;
        for (int i = 0; i < bytes.length; i++) {
            num = bytes[i];
            if(num < 0) {
                num += 256;
            }
            if(num < 16){
                hexStr.append("0");
            }
            hexStr.append(Integer.toHexString(num));
        }
        return hexStr.toString().toUpperCase();
    }

}
